package model.entity;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import model.entity.container.ProductContainer;

import common.Barcode;
import common.Result;
import common.Size;

/**
 * Represents a class of items, any number of which may or may not exist in inventory at any given
 * time.
 * 
 * @author Trevor Dixon, Paula Chen
 * 
 */
@SuppressWarnings("serial")
public class Product extends Entity
{
  private final Barcode          barcode;
  private final Date             creationDate;
  private String                 description;
  private Size                   size;
  private int                    shelfLife;
  private int                    threeMonthSupply;
  private List<ProductContainer> productContainers;

  public Product()
  {
    barcode = new Barcode();
    description = "";
    size = new Size();
    creationDate = new Date();
    shelfLife = 0;
    threeMonthSupply = 0;
    productContainers = new ArrayList<ProductContainer>();
  }

  /**
   * @param barcode
   *          The manufacture-assigned barcode associated with this product.
   * 
   * @param description
   *          The common name for this product.
   * 
   * @param size
   *          The unit and measure of the weight, volume, or mass at which the product is sold.
   * 
   * @param shelfLife
   *          The number of days an instance (i.e. an <code>Item</code>) of this product can remain
   *          in storage before it becomes unusable.
   * 
   *          {@pre barcode != null} {@pre description.length() <= 255} {@pre size != null}
   * 
   */
  public Product(Barcode barcode, String description, Size size, int shelfLife,
      int threeMonthSupply)
  {
    this.barcode = barcode;
    this.description = description;
    this.size = size;
    creationDate = new Date();
    this.shelfLife = shelfLife;
    this.threeMonthSupply = threeMonthSupply;
    productContainers = new ArrayList<ProductContainer>();
  }

  /**
   * @param productContainer
   *          The <code>ProductContainer</code> to which this product will belong.
   * @return A result, indicating whether this operation was successful or not.
   * 
   *         {@pre productContainer != null}
   */
  public Result addToProductContainer(ProductContainer productContainer)
  {
    Result validAdd = validateAddToProductContainer(productContainer);
    if (!validAdd.getStatus())
    {
      return validAdd;
    }
    productContainers.add(productContainer);
    return new Result(true);
  }

  /**
   * @param productContainer
   *          The <code>ProductContainer</code> to which this product will be removed from.
   * @return A result, indicating whether this operation was successful or not.
   * 
   *         {@pre productContainer != null}
   */
  public Result removeFromProductContainer(ProductContainer productContainer)
  {
    Result validRemove = validateRemoveFromProductContainer(productContainer);
    if (!validRemove.getStatus())
    {
      return validRemove;
    }
    productContainers.remove(productContainer);
    return new Result(true);
  }

  /**
   * @param productContainer
   * @return A result, indicating whether it is validate to add.
   * 
   *         {@pre productContainer != null}
   */
  public Result validateAddToProductContainer(ProductContainer productContainer)
  {
    if (productContainer == null)
    {
      return new Result(false, "Null product.");
    }
    if (productContainers.contains(productContainer))
    {
      return new Result(false, "Product already in the Container.");
    }
    return new Result(true);
  }

  /**
   * @param productContainer
   * @return A result, indicating whether it is validate to remove.
   * 
   *         {@pre productContainer != null}
   */
  public Result validateRemoveFromProductContainer(ProductContainer productContainer)
  {
    if (productContainer == null)
    {
      return new Result(false, "Null product.");
    }
    if (!productContainers.contains(productContainer))
    {
      return new Result(false, "Product not in the Container.");
    }
    return new Result(true);
  }

  public String getDescription()
  {
    return description;
  }

  public Result setDescription(String description)
  {
    Result validDescription = validateDescription(description);
    if (!validDescription.getStatus())
    {
      return validDescription;
    }
    this.description = description;
    return new Result(true);
  }

  public Size getSize()
  {
    return size;
  }

  public Result setSize(Size size)
  {
    Result validSize = Size.validateSize(size);
    if (!validSize.getStatus())
    {
      return validSize;
    }
    this.size = size;
    return new Result(true);
  }

  public int getShelfLife()
  {
    return shelfLife;
  }

  public Result setShelfLife(int shelfLife)
  {
    Result validShelfLife = validateShelfLife(shelfLife);
    if (!validShelfLife.getStatus())
    {
      return validShelfLife;
    }
    this.shelfLife = shelfLife;
    return new Result(true);
  }

  public int getThreeMonthSupply()
  {
    return threeMonthSupply;
  }

  public Result setThreeMonthSupply(int threeMonthSupply)
  {
    Result validThreeMonthSupply = validateThreeMonthSupply(threeMonthSupply);
    if (!validThreeMonthSupply.getStatus())
    {
      return validThreeMonthSupply;
    }
    this.threeMonthSupply = threeMonthSupply;
    return new Result(true);
  }

  public Barcode getBarcode()
  {
    return barcode;
  }

  public Date getCreationDate()
  {
    return creationDate;
  }

  public int getSizeOfProductContainers()
  {
    return productContainers.size();
  }

  public Result validateDescription(String description)
  {
    if (description == null || description == "")
    {
      return new Result(false, "Empty description.");
    }
    return new Result(true);
  }

  public Result validateShelfLife(int shelfLife)
  {
    if (shelfLife < 0)
    {
      return new Result(false, "Negative shelf life.");
    }
    return new Result(true);
  }

  public Result validateThreeMonthSupply(int threeMonthSupply)
  {
    if (threeMonthSupply < 0)
    {
      return new Result(false, "Negative 3-month supply.");
    }
    return new Result(true);
  }

  public Result validateProduct()
  {
    Result validSize = Size.validateSize(size);
    Result validShelfLife = validateShelfLife(shelfLife);
    Result validThreeMonthSupply = validateThreeMonthSupply(threeMonthSupply);
    if (!validSize.getStatus())
    {
      return new Result(false, "Invalid size");
    }
    if (!validShelfLife.getStatus())
    {
      return new Result(false, "Invalid shelf life");
    }
    if (!validThreeMonthSupply.getStatus())
    {
      return new Result(false, "Invalid 3 month supply");
    }
    return new Result(true);
  }

  @Override
  public boolean equals(Object obj)
  {
    if (this == obj)
      return true;
    if (getClass() != obj.getClass())
      return false;
    Product other = (Product) obj;
    if (barcode == null)
    {
      if (other.barcode != null)
        return false;
    }
    else if (!barcode.equals(other.barcode))
      return false;
    return true;
  }

  @Override
  public String toString()
  {
    return "Product [barcode=" + barcode + ", creationDate=" + creationDate + ", description="
        + description + ", size=" + size + ", shelfLife=" + shelfLife + ", threeMonthSupply="
        + threeMonthSupply + ", productContainers=" + productContainers + "]";
  }

}
